package gateway

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"sync"
	"time"
	authutils "gitee.com/zxs-micro/zxs-micro-auth/grpc/authserver/util"
	businessutil "gitee.com/zxs-micro/zxs-micro-business/grpc/businesses/utils"
	"gitee.com/zxs-micro/zxs-micro-common/grpc/common"
	commonmodel "gitee.com/zxs-micro/zxs-micro-common/grpc/common/model"
	utils2 "gitee.com/zxs-micro/zxs-micro-common/grpc/common/utils"
	"gitee.com/zxs-micro/zxs-micro-fuwufaxian/grpc/fwfxserver"
	"gitee.com/zxs-micro/zxs-micro-gateway/grpc/gateway/utils"
	"gitee.com/zxs-micro/zxs-micro-common/log"
	"gitee.com/zxs-micro/zxs-micro-gateway/protos/gateway"
	"gitee.com/zxs-micro/zxs-micro-fuwufaxian/model"
)

type GatewayService struct {
	fuwulock   sync.Mutex
	loadlock   sync.Mutex
	fuwulist   map[string]map[string]model.Fuwu
	Loadlist   map[string]map[string]commonmodel.LoadModel
	choiceList map[string]utils.Chooser
	finished   bool
	common.CommonService
}

var g *GatewayService
var nlock sync.Mutex

func NewGatewayService() *GatewayService {
	nlock.Lock()
	defer nlock.Unlock()
	if g == nil {
		g = new(GatewayService)
		g.fuwulist = make(map[string]map[string]model.Fuwu)
		g.Loadlist = make(map[string]map[string]commonmodel.LoadModel)
		g.choiceList = make(map[string]utils.Chooser)
	}
	return g
}

func (g *GatewayService) GetFuwu(types, id string) model.Fuwu {
	g.fuwulock.Lock()
	defer g.fuwulock.Unlock()
	return g.fuwulist[types][id]
}

func (g *GatewayService) LoadAndExec(ctx context.Context, req *gateway.LoadExecRequest) (*gateway.LoadExecResult, error) {
	authid, err := g.getChoiceByType("auths")
	if err != nil {
		return nil, errors.New("请先添加auths服务！")
	}
	fw := g.GetFuwu("auths", authid)

	c, err := authutils.GenerateAuthClient(strings.Split(fw.RemoteIp, "/")[0])
	if err != nil {
		return nil, err
	}
	err = c.CheckAuths(req.Token, req.ExecUrl)
	if err != nil {
		return nil, err
	}
	types := strings.Split(req.ExecUrl, "/")[0]
	fwid, err := g.getChoiceByType(types)
	if err != nil {
		log.Log.Error(err)
		return nil, fmt.Errorf("请先添加%s服务！", types)
	}
	fw1 := g.GetFuwu(types, fwid)
	bcli, err := businessutil.GenerateBussClients(strings.Split(fw1.RemoteIp, "/")[0])
	result, err := bcli.ExecBusiness2Bus(req.Params, req.Token, req.ExecUrl)
	if err != nil {
		return nil, err
	}
	return &gateway.LoadExecResult{
		Payload: result,
	}, nil
}

func (g *GatewayService) getChoiceByType(types string) (string, error) {
	var r1 utils.Chooser
	var ok bool
	g.loadlock.Lock()
	r1, ok = g.choiceList[types]
	g.loadlock.Unlock()

	if !ok {
		return "", fmt.Errorf("暂时没有%s类型的服务！", types)
	}

	return r1.Pick(), nil
}

func (g *GatewayService) StartGateway(remoteip string, err chan error) {
	//每隔5秒同步一次服务列表，并通过列表同步服务器状态
	for {
		<-time.After(time.Second * 5)
		fx, err := fwfxserver.GenerateFwfxClients(remoteip)
		if err != nil {
			log.Log.Error("同步服务信息出现错误：", err.Error())
			continue
		}
		bs, err := fx.GetExistServer("*")
		if err != nil {
			log.Log.Error("同步服务信息出现错误：", err.Error())
			continue
		}
		var fw map[string]map[string]model.Fuwu
		err = json.Unmarshal(bs, &fw)
		if err != nil {
			log.Log.Error("未知错误：", err.Error())
			continue
		}
		g.fuwulock.Lock()
		g.fuwulist = fw
		g.fuwulock.Unlock()
		tmpload := make(map[string]map[string]commonmodel.LoadModel)
		var tlock sync.Mutex
		var wg1 sync.WaitGroup
		for k, v := range fw {
			wg1.Add(1)
			go func(k1 string, v1 map[string]model.Fuwu) {
				defer wg1.Done()
				tlock.Lock()
				tmpload[k1] = make(map[string]commonmodel.LoadModel)
				tlock.Unlock()
				for k2, v2 := range v1 {
					tlock.Lock()
					tmpload[k1][k2] = commonmodel.LoadModel{Ip: v2.RemoteIp}
					tlock.Unlock()
					cm, err := utils2.GenerateMicroClients(strings.Split(v2.RemoteIp, "/")[0])
					if err != nil {
						//fmt.Println(err.Error())
						log.Log.Errorf("服务[%s]获取服务器状态出现异常：%s\n", v2.RemoteIp, err.Error())
						continue
					}
					loadbs, err := cm.GetServiceLoad()
					if err != nil {
						//fmt.Println(err.Error())
						log.Log.Errorf("服务[%s]获取服务器状态出现异常：%s\n", v2.RemoteIp, err.Error())
						continue
					}
					var lm commonmodel.LoadModel
					err = json.Unmarshal(loadbs, &lm)
					//fmt.Println(err)
					tlock.Lock()
					tmpload[k1][k2] = lm
					tlock.Unlock()
				}
			}(k, v)
		}
		wg1.Wait()

		tmpChoices := make(map[string]utils.Chooser)
		for types, fwmaps := range tmpload {
			m1 := make([]utils.Choice, 0, 0)
			for id, fw := range fwmaps {
				var c utils.Choice
				c.Item = id
				//loadavg越高，机器性能占用越高，选择的权重应当越低
				c.Weight = uint(1000 / fw.LoadAvg)
				m1 = append(m1, c)
			}
			tmpChoices[types] = utils.NewChooser(m1...)
		}

		g.loadlock.Lock()
		g.Loadlist = tmpload
		g.choiceList = tmpChoices
		g.loadlock.Unlock()
	}
}
